#include <qlib/db/db.h>
#include <qlib/project/QEDA_Internals.h>
#include <qlib/project/project.h>
#include <spdlog/spdlog.h>


namespace qlib {
QEDA_Internals* QEDA_Internals::qeda = nullptr;
QEDA_Internals::QEDA_Internals() {}

int QEDA_Internals::createProject(const std::string& describe, const std::string& name) {
  for (auto pro : m_projects) {
    if (pro->m_describe == describe && pro->m_name == name)
      return 1;
  }
  m_projects.emplace_back(new qlib::Project(describe, name));
  return 0;
}

std::shared_ptr<Project> QEDA_Internals::findProject(const std::string& describe, const std::string& name) {
  for (auto pro : m_projects) {
    if (pro->m_describe == describe && pro->m_name == name)
      return pro;
  }
  return nullptr;
}

void QEDA_Internals::removeProject(std::shared_ptr<Project> project) {
  for (auto itr = m_projects.begin(); itr != m_projects.end();) {
    std::shared_ptr<Project> pro = *itr;
    if (pro == project) {
      m_projects.erase(itr);
      break;
    } else {
      itr++;
    }
  }
}

std::shared_ptr<Library> QEDA_Internals::loadOasFile(const std::string& describe, const std::string& project_name, const std::string& libaray_name,
                                                     const std::string& fileName) {
  auto pro = findProject(describe, project_name);
  if (pro) {
    qlib::ErrorCode err = qlib::ErrorCode::NoError;
    qlib::Library::s_ptr library = qlib::DB::read_oas(fileName, 1e-6, 0, &err);
    if (err != qlib::ErrorCode::NoError) {
      SPDLOG_INFO("[QEDA_Internals]:qlib::DB::read_oas error");
      return nullptr;
    }
    library->name = libaray_name;

    for (const auto& cell : library->cells) {
      // 调试用
      //std::vector<entt::entity> cell_entity_list = qbe::auxiliary::getCellAllGraph(cell);
    }

    pro->add_library(library);
    return library;
  }
  return nullptr;
}

int QEDA_Internals::writeOasFile(const std::string& describe, const std::string& project_name, const std::string& libaray_name,
                                 const std::string& name) {
  std::shared_ptr<Library> lib = findLibrary(describe, project_name, libaray_name);
  if (lib) {
    std::time_t rawtime;
    std::time(&rawtime);
    tm* timeinfo = std::localtime(&rawtime);

    for (const auto& cell : lib->cells) {
      // 调试用
      //std::vector<entt::entity> cell_entity_list = qbe::auxiliary::getCellAllGraph(cell);
    }

    qlib::ErrorCode err = qlib::DB::write_oas(lib, name);
    return 0;
  }
  return 1;
}

std::shared_ptr<Library> QEDA_Internals::loadGdsFile(const std::string& describe, const std::string& project_name, const std::string& fileName) {
  auto pro = findProject(describe, project_name);
  if (pro) {
    qlib::ErrorCode err = qlib::ErrorCode::NoError;
    qlib::Library::s_ptr library = qlib::DB::read_gds(fileName, 1e-6, 0, &err);
    pro->add_library(library);
    return library;
  }
  return nullptr;
}

int QEDA_Internals::writeGdsFile(const std::string& describe, const std::string& project_name, const std::string& libaray_name,
                                 const std::string& name) {
  std::shared_ptr<Library> lib = findLibrary(describe, project_name, libaray_name);
  if (lib) {
    std::time_t rawtime;
    std::time(&rawtime);
    tm* timeinfo = std::localtime(&rawtime);
    qlib::ErrorCode err = qlib::DB::write_gds(lib, name, 199, timeinfo);
    return 0;
  }
  return 1;
}

std::shared_ptr<Library> QEDA_Internals::findLibrary(const std::string& describe, const std::string& project_name, const std::string& libaray_name) {
  auto pro = findProject(describe, project_name);
  if (pro) {
    if (pro->m_librarys.size() == 0) {
      return nullptr;
    }
    auto libs = pro->m_librarys;
    for (auto lib : libs) {
      if (lib && lib->name == libaray_name)
        return lib;
    }
  }
  return nullptr;
}

std::shared_ptr<Library> QEDA_Internals::findLibrary(std::shared_ptr<Cell> t_cell) {
  for (auto pro : m_projects) {
    if (pro->m_librarys.size() == 0) {
      continue;
    }
    auto libs = pro->m_librarys;
    for (auto lib : libs) {
      auto cells = lib->cells;
      for (auto cell : cells) {
        if (cell == t_cell) {
          return lib;
        }
      }
    }
  }
  return std::shared_ptr<Library>();
}

void QEDA_Internals::deleteLibrary(std::shared_ptr<Library> t_lib) {
  for (auto pro : m_projects) {
    if (pro->m_librarys.size() == 0) {
      continue;
    }
    auto& libs = pro->m_librarys;
    auto itr = libs.find(t_lib);
    if (itr != libs.end()) {
      libs.erase(itr);
      return;
    }
  }
}

std::shared_ptr<Cell> QEDA_Internals::getCell(const std::string& describe, const std::string& project_name, const std::string& libaray_name,
                                              const std::string& cell_name) {
  std::shared_ptr<Library> lib = findLibrary(describe, project_name, libaray_name);
  if (lib) {
    for (auto cell : lib->cells) {
      if (cell->name == cell_name)
        return cell;
    }
  }
  return std::shared_ptr<Cell>();
}

QEDA_Internals::~QEDA_Internals() {}

QEDA_Internals* QEDA_Internals::getInstance() {
  if (qeda == nullptr) {
    qeda = new QEDA_Internals();
  }
  return qeda;
}
}  // namespace qlib